package com.msi.visualization;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.MapTransformer;

import edu.uci.ics.jung.algorithms.layout.FRLayout2;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
import com.msi.Vertex;

public class GraphVisualization extends VisualizationViewer<Integer, String> {
    private class EdgeTransf implements Transformer<String, Paint> {
        private Integer vertex;

        public EdgeTransf(Integer vertex) {
            this.vertex = vertex;
        }

        @Override
        public Paint transform(String s) {
            if (g.getInEdges(vertex).contains(s))
                return Color.green;
            return null;
        }

    }

    private static GraphVisualization instance;
    private static final int LAYOUT_HEIGHT = 560;

    private static final int LAYOUT_WIDTH = 560;
    private static final long serialVersionUID = 1L;

    public static GraphVisualization createGraphVisualization() {
        Graph<Integer, String> g = new SparseMultigraph<Integer, String>();
        Layout<Integer, String> layout = new FRLayout2(g);

        instance = new GraphVisualization(g, layout);
        return instance;
    }

    public static GraphVisualization getInstance() {
        if (instance == null)
            createGraphVisualization();
        return instance;
    }

    private Graph<Integer, String> g;

    private com.msi.Graph graph = null;

    private Layout<Integer, String> layout;

    private GraphVisualization(final Graph<Integer, String> g,
            Layout<Integer, String> layout) {
        super(layout);
        this.g = g;
        this.layout = layout;

        getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
        getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);

        setGraphMouse(new DefaultModalGraphMouse<Integer, String>());

        addGraphMouseListener(new GraphMouseListener<Integer>() {

            @Override
            public void graphClicked(Integer arg0, MouseEvent arg1) {
                // do nothing
            }

            @Override
            public void graphPressed(final Integer vertex, MouseEvent arg1) {
                getRenderContext().setEdgeDrawPaintTransformer(
                        new EdgeTransf(vertex));
                repaint();
            }

            @Override
            public void graphReleased(Integer arg0, MouseEvent arg1) {
                // do nothing
            }
        });
    }

    public void buildGraph(com.msi.Graph graph) {
        clearGraph();
        this.graph = graph;
        initializeCoordinates();
        List<Vertex> vertexes = graph.getVerticesSortedByDegree();
        for (Vertex v : vertexes) {
            g.addVertex(v.getId());
        }
        for (Vertex v : vertexes) {
            List<Vertex> neighborhood = v.getNeighborhood();
            for (Vertex v2 : neighborhood) {
                if (!g.isNeighbor(v.getId(), v2.getId())) {
                    g.addEdge(v.getId() + "-" + v2.getId(), v.getId(),
                            v2.getId());
                }
            }
        }
    }

    public void changeLayout(Layout<Integer, String> layout) {
        setGraphLayout(layout);
        this.layout = layout;
    }

    public void useAutoLayout() {
        changeLayout(new FRLayout2(g));
    }

    private void clearGraph() {
        getRenderContext().setEdgeDrawPaintTransformer(
                getDefaultEdgeTransformer());
        getRenderContext().setVertexFillPaintTransformer(
                getDefaultColorTransformer());
        g = new SparseMultigraph<Integer, String>();
        graph = null;
    }

    private Transformer<Integer, Paint> getDefaultColorTransformer() {
        return new Transformer<Integer, Paint>() {

            @Override
            public Paint transform(Integer arg0) {
                return Color.red;
            }
        };
    }

    private Transformer<String, Paint> getDefaultEdgeTransformer() {
        return new Transformer<String, Paint>() {

            @Override
            public Paint transform(String arg0) {
                return Color.black;
            }
        };
    }

    public com.msi.Graph getGraph() {
        return graph;
    }

    private void initializeCoordinates() {
        if (graph.hasCoordinates()) {
            Transformer<Integer, Point2D> tr = MapTransformer.getInstance(graph
                    .getCoordinateMap());
            changeLayout(new StaticLayout(g, tr, new Dimension(100, 100)));
        } else {
            changeLayout(new FRLayout2(g));
        }
    }

    public void updateColors(final MsiColor impl) {
        final Map<Integer, Color> colors = new ColorMap(
                impl.getNumberOfColors());
        Transformer<Integer, Paint> vertexPaint = new Transformer<Integer, Paint>() {
            @Override
            public Paint transform(Integer i) {
                return colors.get(graph.getVertex(i).getColor());
            }
        };
        getRenderContext().setVertexFillPaintTransformer(vertexPaint);
    }
}
